﻿using Microsoft.Extensions.DependencyInjection;
using Modules.BankSpecialAccount.Entitys;
using Modules.BankSpecialAccount;
using Modules.CheckIn;
using Modules.Project;
using PmSoft;
using static Modules.BankSpecialAccount.BankSpecialAccountConstants;
using static Modules.Person.PersonConstants;
using Modules.CheckInCount;
using Modules.System.Settings.Entitys;
using Modules.System.Settings;

namespace Modules.Reports;

/// <summary>
/// 项目月统计报表服务
/// </summary>
[UseDI(ServiceLifetime.Scoped)]
public class ReportProjectMonthService
{
    private readonly ReportProjectMonthRepository reportProjectMonthRepository;
    private readonly ProjectService projectService;
    private readonly BankSpecialAccountService projectSpecialAccountService;
    private readonly ProjectStatusChangeRecordService projectStatusChangeRecordService;
    private readonly CheckinWorkHoursService checkinWorkHoursService;
    private readonly ProjectGroupMemberService projectGroupMemberService;
    private readonly BankSpecialAccountStatisticMonthService bankSpecialAccountStatisticMonthService;
    private readonly CheckInStatisticService checkInStatisticService;
    private readonly CheckInCountService checkInCountService;
    private readonly CheckinWageStatusSettings checkinWageStatusSettings;

    public ReportProjectMonthService(ReportProjectMonthRepository reportProjectMonthRepository,
        ProjectService projectService,
        BankSpecialAccountService projectSpecialAccountService,
        ProjectStatusChangeRecordService projectStatusChangeRecordService,
        CheckinWorkHoursService checkinWorkHoursService,
        ProjectGroupMemberService projectGroupMemberService,
        BankSpecialAccountStatisticMonthService bankSpecialAccountStatisticMonthService,
        CheckInStatisticService checkInStatisticService,
        CheckInCountService checkInCountService,
        SettingManager<CheckinWageStatusSettings> CheckinWageStatusSettingManager)
    {
        this.reportProjectMonthRepository = reportProjectMonthRepository;
        this.projectService = projectService;
        this.projectSpecialAccountService = projectSpecialAccountService;
        this.projectStatusChangeRecordService = projectStatusChangeRecordService;
        this.checkinWorkHoursService = checkinWorkHoursService;
        this.projectGroupMemberService = projectGroupMemberService;
        this.bankSpecialAccountStatisticMonthService = bankSpecialAccountStatisticMonthService;
        this.checkInStatisticService = checkInStatisticService;
        this.checkInCountService = checkInCountService;
        this.checkinWageStatusSettings = CheckinWageStatusSettingManager.Get();
    }

    /// <summary>
    /// 生成项目月报表
    /// </summary>
    /// <param name="projectId"></param>

    /// <exception cref="ArgumentNullException"></exception>
    public void Generate(int projectId, DateTime date)
    {
        var projectInfo = projectService.GetProjectInfo(projectId);
        if (projectInfo == null)
            throw new ArgumentNullException(nameof(projectInfo));

        int month = Convert.ToInt32(date.ToString("yyyyMM"));

        DateTime beginDate = new DateTime(date.Year, date.Month, 1);
        DateTime endDate = beginDate.AddMonths(1);
        if (endDate > DateTime.Now.Date)
            endDate = DateTime.Now.Date;

        ReportProjectMonth entity = new();
        entity.UpdateTime = DateTime.Now;
        entity.Month = month;
        entity.ProjectId = projectInfo.ProjectId;
        entity.ProjectName = projectInfo.Name;

        //项目所有成员
        var members = projectGroupMemberService.GetProjectGroupMembersByProjectId(projectInfo.ProjectId);
        //项目考勤统计
        var statisticInfo = checkInStatisticService.GetProjectCheckinStatisticInfo(CountStageType.Month, projectId, date);
        //专户落实状态
        var projectSpecialAccounts = projectSpecialAccountService.GetProjectSpecialAccountByProjectId(projectInfo.ProjectId);
        entity.SpecialAccountStatus = projectSpecialAccounts.Any(m => m.AccountStatus == 专户状态.正常) ? 落实状态.已落实 : 落实状态.未落实; //只要有一个正常专户就算落实

        //当月在建和停工天数
        var projectStatusChangeRecords = projectStatusChangeRecordService.GetProjectStatusChangeRecordsByProjectId(projectInfo.ProjectId);
        entity.DaysThisMonthUnderConstruction = ProjectStatusCalculator.CalculateDaysOfStatus(projectStatusChangeRecords,ProjectConstants.项目状态.在建, month);
        entity.DaysThisMonthShutDown = ProjectStatusCalculator.CalculateDaysOfStatus(projectStatusChangeRecords,ProjectConstants.项目状态.停工, month);

        #region 考勤状态

        entity.CheckinStatus = 落实状态.无需核实;
        //if (entity.DaysThisMonthUnderConstruction > 0)
        //{
        int checkinDaysCount = checkInStatisticService.GetProjectCheckinDays(projectId, date.Year, date.Month);
        entity.CheckinStatus = GetCheckinStatus(entity.DaysThisMonthUnderConstruction, checkinDaysCount);
        entity.NumberAttendanceRequired = members.Count();
        entity.ActualAttendance = statisticInfo.CheckinPersonCount;
        //}

        #endregion 考勤状态

        entity.WageStatus = 落实状态.无需核实;

        #region 进账状态

        var bankStatistic = new BankSpecialAccountStatisticMonth();
        var projectSpecialAccount = projectSpecialAccounts.FirstOrDefault(m => m.AccountStatus == 专户状态.正常);// 后期要统计所有有效专户合计
        if (projectSpecialAccount != null && !string.IsNullOrEmpty(projectSpecialAccount.BankNo))
        {
            bankStatistic = bankSpecialAccountStatisticMonthService.GetProjectSpecialAccountStatisticMonthByBankAccountMonth(projectSpecialAccount.BankNo, entity.Month) ?? new BankSpecialAccountStatisticMonth();
        }
        entity.CurrentActualConstructionMonths = Convert.ToDecimal((endDate - projectInfo.ContractStartDate).Days) / Convert.ToDecimal(30);
        if (!projectInfo.ContractEndDate.HasValue) entity.TotalDuration = 0;
        else
            entity.TotalDuration = (projectInfo.ContractEndDate.Value - projectInfo.ContractStartDate).Days / 30;
        if (entity.TotalDuration > 0)
            entity.TotalAmountDue = Math.Round(Convert.ToDecimal(projectInfo.ContractAmount) * Convert.ToDecimal(0.2) / entity.TotalDuration * entity.CurrentActualConstructionMonths, 2);
        if (entity.TotalAmountDue < 0)
            entity.TotalAmountDue = 0;
        entity.TotalActualReceipts = Math.Round((bankStatistic.IncomeAmountEnd ?? 0) - (bankStatistic.IncomeAmountStart ?? 0), 2);
        if (entity.TotalDuration > 0)
            entity.TheoreticalBalance = Math.Round(Convert.ToDecimal(projectInfo.ContractAmount) * Convert.ToDecimal(0.2) / entity.TotalDuration, 2);
        entity.OnlineAmount = Math.Round((bankStatistic.OnlinePaymentAmountEnd ?? 0) - (bankStatistic.OnlinePaymentAmountStart ?? 0), 2);
        entity.OfflineAmount = Math.Round((bankStatistic.OfflinePaymentAmountEnd ?? 0) - (bankStatistic.OfflinePaymentAmountStart ?? 0), 2);
        if (entity.TotalAmountDue == entity.TotalActualReceipts)
            entity.IncomeStatus = 落实状态.已落实;
        else
            entity.IncomeStatus = 落实状态.未落实;

        #endregion 进账状态

        #region 关键岗位考勤状态

        var managerMembers = members.Where(a => a.ProjectId == projectInfo.ProjectId && (a.PostCategory == 管理岗位类型.项目经理 || a.PostCategory == 管理岗位类型.安全员 || a.PostCategory == 管理岗位类型.技术负责人 || a.PostCategory == 管理岗位类型.总监理工程师));
        //checkInCountService.GetCountPerday(CountTenantType.Member, CountType.WorkDays, startTime.Date, managerMembers.Select(m => m.Id));

        //  entity.CheckinStatus = GetCheckinStatus(managerMembers.Count(), checkinWorkHours, projectInfo.ProjectId, true);

        #endregion 关键岗位考勤状态

        var reportProjectMonths = GetReportProjectMonthByProjectId(projectInfo.ProjectId);

        entity.DaysUnderConstruction = reportProjectMonths.Where(m => m.Month < month).Sum(a => a.DaysUnderConstruction);//总计小于统计月的数据
        entity.DaysShutDown = reportProjectMonths.Where(m => m.Month < month).Sum(a => a.DaysUnderConstruction);
        entity.NumberAgencyTransactions = 0;
        entity.PaymentAmount = 0;
        entity.NumberProxyRecipients = 0;
        entity.Balance = projectSpecialAccount?.Balance ?? 0;

        #region 项目经理考勤天数

        int? memberid = managerMembers.FirstOrDefault(a => a.PostCategory == 管理岗位类型.项目经理)?.Id;
        if (memberid.HasValue)
        {
            var countPerday = checkInCountService.GetCountPermonth(CountTenantType.Member, CountType.WorkDays, date, memberid.Value);
            entity.ProjectManagerAttendanceDays = countPerday?.StatisticsCount ?? 0;
        }
        #endregion 项目经理考勤天数

        #region 技术负责人考勤天数

        memberid = managerMembers.FirstOrDefault(a => a.PostCategory == 管理岗位类型.技术负责人)?.Id;
        if (memberid.HasValue)
        {
            var countPerday = checkInCountService.GetCountPermonth(CountTenantType.Member, CountType.WorkDays, date, memberid.Value);
            entity.TechnicalManagerAttendanceDays = countPerday?.StatisticsCount ?? 0;
        }
        #endregion 技术负责人考勤天数

        #region 安全员考勤天数

        memberid = managerMembers.FirstOrDefault(a => a.PostCategory == 管理岗位类型.安全员)?.Id;
        if (memberid.HasValue)
        {
            var countPerday = checkInCountService.GetCountPermonth(CountTenantType.Member, CountType.WorkDays, date, memberid.Value);
            entity.SafetyOfficerAttendanceDays = countPerday?.StatisticsCount ?? 0;
        }

        #endregion 安全员考勤天数

        #region 总监理工程师考勤天数

        memberid = managerMembers.FirstOrDefault(a => a.PostCategory == 管理岗位类型.总监理工程师)?.Id;
        if (memberid.HasValue)
        {
            var countPerday = checkInCountService.GetCountPermonth(CountTenantType.Member, CountType.WorkDays, date, memberid.Value);
            entity.SupervisionAttendanceDays = countPerday?.StatisticsCount ?? 0;
        }
        #endregion 总监理工程师考勤天数

        CreateOrUpdate(entity);
    }

    /// <summary>
    /// 获取考勤落实状态
    /// </summary>
    /// <param name="buildingCount">项目在建天数</param>
    /// <param name="checkinDaysCount">项目考勤天数</param>
    /// <returns></returns>
    private 落实状态 GetCheckinStatus(int buildingCount, int checkinDaysCount)
    {
        if (buildingCount > 0)
        {
            if (checkinDaysCount == 0)
                return 落实状态.未落实;
            else
            {
                var checkinRate = buildingCount / (decimal)buildingCount;
                if (checkinRate >= checkinWageStatusSettings.ImplementedValue)
                    return 落实状态.已落实;
                else if (checkinRate < checkinWageStatusSettings.ImplementedValue && checkinRate >= checkinWageStatusSettings.SemiImplementationValue)
                    return 落实状态.半落实;
                else //checkinRate==0
                    return 落实状态.未落实;
            }
        }
        else return 落实状态.无需核实;
    }


    /// <summary>
    /// 项目月统计报表表单详情
    /// </summary>
    /// <param name="id">主键</param>
    /// <returns></returns>
    public ReportProjectMonth? Get(int id)
    {
        return reportProjectMonthRepository.Get(id);
    }

    /// <summary>
    /// 批量删除，根据id
    /// </summary>
    /// <param name="ids">id的集合</param>
    public void Deleted(IEnumerable<int> ids)
    {
        foreach (int id in ids)
        {
            Delete(id);
        }
    }

    /// <summary>
    /// 单个删除
    /// </summary>
    /// <param name="id">主键</param>
    public void Delete(int id)
    {
        reportProjectMonthRepository.DeleteByEntityId(id);
    }

    /// <summary>
    /// 创建或更新项目月统计报表
    /// </summary>
    /// <param name="entity">实体对象</param>
    public void CreateOrUpdate(ReportProjectMonth entity)
    {
        reportProjectMonthRepository.CreateOrUpdate(entity);
    }

    /// <summary>
    /// 项目月统计报表分页列表
    /// </summary>
    /// <param name="query">查询参数</param>
    /// <param name="pageIndex">当前页码</param>
    /// <param name="pageSize">每页显示条数</param>
    /// <returns></returns>
    public IPagedList<ReportProjectMonth> GetReportProjectMonths(ReportProjectMonthQuery query, int pageIndex, int pageSize)
    {
        return reportProjectMonthRepository.GetReportProjectMonths(query, pageIndex, pageSize);
    }

    /// <summary>
    /// 获取项目下指定月份的报表
    /// </summary>
    /// <param name="projectId"></param>
    /// <param name="month"></param>
    /// <returns></returns>
    public ReportProjectMonth? GetReportProjectMonthByProjectIdAndMonth(int projectId, int month)
    {
        return reportProjectMonthRepository.GetReportProjectMonthByProjectId(projectId).FirstOrDefault(a => a.Month == month);
    }

    /// <summary>
    ///  获取指定月份和落实状态的项目
    ///  数据量大，非高频使用
    /// </summary>
    /// <param name="month"></param>
    /// <returns></returns>
    public IEnumerable<ReportProjectMonth> GetReportProjectsMonthsInWageStatus(int month, IEnumerable<落实状态> wageStatus)
    {
        return reportProjectMonthRepository.GetReportProjectsMonthsInWageStatus(month, wageStatus);
    }

    /// <summary>
    /// 获取指定项目ID所有月报表
    /// </summary>
    /// <param name="projectId"></param>
    /// <returns></returns>
    public IEnumerable<ReportProjectMonth> GetReportProjectMonthByProjectId(int projectId)
    {
        return reportProjectMonthRepository.GetReportProjectMonthByProjectId(projectId);
    }

    /// <summary>
    /// 查没有今日没有更新的项目月报表的项目ID集合
    /// PS:排除今日更新过的项目
    /// </summary>
    /// <param name="month"></param>
    /// <param name="limit"></param>
    /// <returns></returns>
    public IEnumerable<int> GetProjectIdsInNoUpdate(int month, int limit = 100)
    {
        return reportProjectMonthRepository.GetProjectIdsInNoUpdate(month, limit);
    }

    //public IEnumerable<ReportProjectMonth> GetAll()
    //{
    //    return reportProjectMonthRepository.GetAll();
    //}

    /// <summary>
    /// </summary>
    /// <param name="projectIds"></param>
    /// <returns></returns>
    //public IEnumerable<ReportProjectMonth> GetReportProjectMonthByProjectIds(IEnumerable<int> projectIds)
    //{
    //    return reportProjectMonthRepository.GetReportProjectMonthByProjectIds(projectIds);
    //}

    /// <summary>
    /// </summary>
    /// <param name="projectId"></param>
    /// <returns></returns>
    //public IEnumerable<ReportProjectMonth> GetReportProjectMonthByProjectId(int projectId)
    //{
    //    return reportProjectMonthRepository.GetReportProjectMonthByProjectId(projectId);
    //}
}